{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Private Predictions with TFE Keras\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Private Prediction using TFE Keras - Serving (Client)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Congratulations! After training your model with normal Keras and securing it with TFE Keras, you are ready to request some private predictions. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/anaconda3/envs/py35_int64/lib/python3.5/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "WARNING:tf_encrypted:Falling back to insecure randomness since the required custom op could not be found for the installed version of TensorFlow. Fix this by compiling custom ops. Missing file was '/Users/yanndupis/Documents/dropoutlabs/tfe_v2/tf-encrypted/tf_encrypted/operations/secure_random/secure_random_module_tf_1.13.1.so'\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import tf_encrypted as tfe\n",
    "\n",
    "from tensorflow.keras.datasets import mnist"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data\n",
    "\n",
    "Here, we preprocess our MNIST data. This is identical to how we preprocessed during training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# input image dimensions\n",
    "img_rows, img_cols = 28, 28\n",
    "\n",
    "# the data, split between train and test sets\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n",
    "\n",
    "x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)\n",
    "x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)\n",
    "input_shape = (img_rows, img_cols, 1)\n",
    "\n",
    "x_train = x_train.astype('float32')\n",
    "x_test = x_test.astype('float32')\n",
    "x_train /= 255\n",
    "x_test /= 255"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up `tfe.serving.QueueClient`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before querying the model, you just have to connect to it. To do so, you can create a client with `tfe.serving.QueueClient`. This creates a TFE queueing server on the client side that connects to the queueing server set up by `tfe.serving.QueueServer` in **b - Secure Model Serving**. The queue will be responsible for secret sharing the plaintext data before submitting the shares in a prediction request.\n",
    "\n",
    "Note that we have to use the same configuration as used by the server, including player configuration and protocol."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = tfe.RemoteConfig.load(\"/tmp/tfe.config\")\n",
    "\n",
    "tfe.set_config(config)\n",
    "tfe.set_protocol(tfe.protocol.SecureNN())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_shape = (1, 28, 28, 1)\n",
    "output_shape = (1, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "client = tfe.serving.QueueClient(\n",
    "    input_shape=input_shape,\n",
    "    output_shape=output_shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:tf_encrypted:Starting session on target 'grpc://localhost:4000' using config graph_options {\n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "sess = tfe.Session(config=config)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Query Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You are ready to get some private predictions! Calling `client.run` will insert the `image` into the queue created above, secret share the data locally, and submit the shares to the model server in **b - Secure Model Serving**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# User inputs\n",
    "num_tests = 3\n",
    "images, expected_labels = x_test[:num_tests], y_test[:num_tests]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The image had label 7 and was correctly classified as 7\n",
      "The image had label 2 and was correctly classified as 2\n",
      "The image had label 1 and was correctly classified as 1\n"
     ]
    }
   ],
   "source": [
    "for image, expected_label in zip(images, expected_labels):\n",
    "    \n",
    "    res = client.run(\n",
    "        sess,\n",
    "        image.reshape(1, 28, 28, 1))\n",
    "    \n",
    "    predicted_label = np.argmax(res)\n",
    "    \n",
    "    print(\"The image had label {} and was {} classified as {}\".format(\n",
    "        expected_label,\n",
    "        \"correctly\" if expected_label == predicted_label else \"incorrectly\",\n",
    "        predicted_label))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is great. You are able to classify these three images correctly! But what's special about these predictions is that you haven't revealed any private information to get this service. The model host never saw your input data or your predictions, and you never downloaded the model. You were able to get private predictions on encrypted data with an encrypted model!\n",
    "\n",
    "Before we rush off to apply this in our own apps, let's quickly go back to **b - Secure Model Serving** to clean up our served model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
